package io.gitee.macxiang.sql;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.UUID;

import com.alibaba.fastjson2.JSON;

import io.gitee.macxiang.utils.utils;

/**
 * 雪花算法ID生成器
 */
public class SnowflakeIdWorker {
  // 起始的时间戳
  private final long START_STMP;

  // 每一部分占用的位数
  private final long SEQUENCE_BIT = 12; // 序列号占用的位数
  private final long MACHINE_BIT = 5; // 机器标识占用的位数
  private final long DATACENTER_BIT = 5;// 数据中心占用的位数

  // 每一部分的最大值
  private final long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
  private final long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
  private final long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);

  // 每一部分向左的位移
  private final long MACHINE_LEFT = SEQUENCE_BIT;
  private final long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
  private final long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;

  private long datacenterId; // 数据中心
  private long machineId; // 机器标识
  private long sequence = 0L; // 序列号
  private long lastStmp = -1L;// 上一次时间戳

  private long getDefaultStartStmp() {
    return System.currentTimeMillis() / 1000;
  }

  /**设置配置信息
   *
   * @param datacenterId  数据中心ID
   * @param machineId     机器ID
   * @throws IllegalArgumentException 如果数据中心ID或机器ID超出允许的范围
   */
  public void initSequence(long datacenterId, long machineId) {
    if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
      throw new IllegalArgumentException("数据中心ID不能大于最大数据中心数或小于0");
    }
    if (machineId > MAX_MACHINE_NUM || machineId < 0) {
      throw new IllegalArgumentException("机器ID不能大于最大机器数或小于0");
    }
    this.datacenterId = datacenterId;
    this.machineId = machineId;

  }

  /** 构造函数 */
  public SnowflakeIdWorker() {
    initSequence(0, 0);
    this.START_STMP = getDefaultStartStmp();
  }

  /**构造函数
   *
   * @param datacenterId  数据中心ID
   * @param machineId     机器ID
   * @param startStmp     起始的时间戳
   * @throws IllegalArgumentException 如果数据中心ID或机器ID超出允许的范围
   */
  public SnowflakeIdWorker(long datacenterId, long machineId, Long startStmp) {
    initSequence(datacenterId, machineId);
    if (startStmp == null || startStmp <= 0) {
      startStmp = getDefaultStartStmp();
    }
    this.START_STMP = startStmp;
  }

  /**构造函数
   *
   * @param datacenterId  数据中心ID
   * @param machineId     机器ID
   * @throws IllegalArgumentException 如果数据中心ID或机器ID超出允许的范围
   */
  public SnowflakeIdWorker(long datacenterId, long machineId) {
    initSequence(datacenterId, machineId);
    this.START_STMP = getDefaultStartStmp();
  }

  /**生成下一个唯一ID
   *
   * @return 生成的唯一ID:长度19
   * @throws RuntimeException 如果时钟回退，拒绝生成ID
   */
  public synchronized long idLong() {
    long currStmp = getNewstmp();
    if (currStmp < lastStmp) {
      throw new RuntimeException("时钟回退，拒绝生成ID");
    }

    if (currStmp == lastStmp) {
      // 相同毫秒内，序列号自增
      sequence = (sequence + 1) & MAX_SEQUENCE;
      // 同一毫秒的序列数已经达到最大
      if (sequence == 0L) {
        currStmp = getNextMill();
      }
    } else {
      // 不同毫秒内，序列号置为0
      sequence = 0L;
    }

    lastStmp = currStmp;

    return (currStmp - START_STMP) << TIMESTMP_LEFT // 时间戳部分
        | datacenterId << DATACENTER_LEFT // 数据中心部分
        | machineId << MACHINE_LEFT // 机器标识部分
        | sequence; // 序列号部分
  }

  /**生成下一个唯一ID
   *
   * @return 生成的唯一ID:长度19
   * @throws RuntimeException 如果时钟回退，拒绝生成ID
   */
  public synchronized String idString() {
    return String.valueOf(idLong());
  }

  /** 获取一个36位的uuid */
  public String id36() {
    return UUID.randomUUID().toString();
  }

  /** 获取一个36位的uuid去除符号- */
  public String id32() {
    return id36().replace("-", "");
  }

  /**取数据特征
   * @param obj     数据
   * @param isSha1  是否返回哈希值
   * @return        如果isSha1为false;则返回md5
   */
  public String toHash(Object obj, boolean isSha1) {
    try {
      MessageDigest md = MessageDigest.getInstance(isSha1 ? "SHA-1" : "MD5");
      if (obj == null) {
        obj = id36();
      }
      md.update(String.valueOf(obj instanceof String ? obj : JSON.toJSONString(obj)).getBytes());
      return String.format("%02X", new BigInteger(1, md.digest()));
    } catch (Exception e) {
      utils.out(e);
    }
    return "";
  }

  /** 获取一个40位长度的随机数 */
  public String id40() {
    return toHash(id32(), true);
  }

  /**获取下一个毫秒的时间戳
   *
   * @return 下一个毫秒的时间戳
   */
  private long getNextMill() {
    long mill = getNewstmp();
    while (mill <= lastStmp) {
      mill = getNewstmp();
    }
    return mill;
  }

  /**获取当前时间戳
   *
   * @return 当前时间戳
   */
  private long getNewstmp() {
    return System.currentTimeMillis();
  }

  public void main(String[] args) {
    SnowflakeIdWorker idWorker = new SnowflakeIdWorker(1, 1);
    System.out.println(String.valueOf(System.currentTimeMillis()).length());
    for (int i = 0; i < 10; i++) {
      long id = idWorker.idLong();
      System.out.println(id + ":" + String.valueOf(id).length());
    }
  }
}
